package com.yunquan.ohana.ui.calendar.sync.syncNew

// Microsoft Graph / MSAL imports
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.view.WindowInsetsControllerCompat
import biweekly.Biweekly
import biweekly.ICalendar
import biweekly.component.VEvent
import com.google.android.gms.auth.GoogleAuthUtil
import com.google.android.gms.auth.UserRecoverableAuthException
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.common.api.Scope
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.microsoft.identity.client.AcquireTokenParameters
import com.microsoft.identity.client.AcquireTokenSilentParameters
import com.microsoft.identity.client.AuthenticationCallback
import com.microsoft.identity.client.IAccount
import com.microsoft.identity.client.IAuthenticationResult
import com.microsoft.identity.client.IMultipleAccountPublicClientApplication
import com.microsoft.identity.client.IPublicClientApplication
import com.microsoft.identity.client.PublicClientApplication
import com.microsoft.identity.client.exception.MsalException
import com.yunquan.ohana.BR
import com.yunquan.ohana.R
import com.yunquan.ohana.base.ui.BaseActivity
import com.yunquan.ohana.databinding.ActivitySyncNewBinding
import com.yunquan.ohana.dialog.TipDialog
import com.yunquan.ohana.entity.ScheduleModelVo
import com.yunquan.ohana.ui.calendar.sync.syncNew.inputURL.InputURLActivity
import com.yunquan.ohana.utils.L
import com.yunquan.ohana.utils.StringUtils
import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors

class SyncNewActivity : BaseActivity<ActivitySyncNewBinding, SyncNewViewModel>() {
    // Google Calendar 只读 Scope
    private val calendarScope = Scope("https://www.googleapis.com/auth/calendar.readonly")

    // Google Sign-In 结果处理
    private val googleSignInLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
        try {
            val account = task.getResult(ApiException::class.java)
            fetchGoogleCalendars(account)
        } catch (e: Exception) {
            showTip("Google Sign-In failed: ${e.localizedMessage ?: e.javaClass.simpleName}")
        }
    }

    private val ioExecutor by lazy { Executors.newSingleThreadExecutor() }

    // MSAL for Microsoft OAuth + Graph API
    private var msalApp: IMultipleAccountPublicClientApplication? = null
    private val msScopes = listOf("User.Read", "Calendars.Read")

    // 是否订阅
    private var isSubscribed = false

    // 输入URL 返回
    private val urlInputLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == 1001) {
            val url = result.data?.getStringExtra("url")
            if (StringUtils.isEmpty(url)) {
                showTip(getString(R.string.please_enter_a_valid_url))
            } else {
                if (isSubscribed) {
                    syncSubscribedCalendar(url!!.trim())
                } else {
                    syncCalendarUrl(url!!.trim())
                }
            }
        }
    }

    override fun initContentView(savedInstanceState: Bundle?): Int {
        return R.layout.activity_sync_new
    }

    override fun initVariableId(): Int {
        return BR.syncNewViewModel
    }

    override fun initParam() {
        super.initParam()
        Locale.setDefault(Locale("en"))
        // 设置背景颜色
        window.statusBarColor = ContextCompat.getColor(this, R.color.white)
        // 设置文字颜色（true = 黑色文字，false = 白色文字）
        val controller = WindowInsetsControllerCompat(window, window.decorView)
        controller.isAppearanceLightStatusBars = true
    }

    override fun initData(savedInstanceState: Bundle?) {
        super.initData(savedInstanceState)
        val bundle = intent.extras
        bundle?.let {
            viewModel!!.mDeviceId = it.getString("deviceId")
            viewModel!!.mRoleId = it.getString("roleId")
        }
        // 绑定点击事件
        binding!!.tvGoogle.setOnClickListener { syncGoogle() }
        binding!!.tvOutlook.setOnClickListener { syncOutlook() }
        binding!!.clIcloud.setOnClickListener { syncICloud() }
        binding!!.tvCalendarUrl.setOnClickListener { promptCalendarUrl(false) }
        binding!!.clSubCalendar.setOnClickListener { promptCalendarUrl(true) }
    }

    override fun initViewObservable() {
        super.initViewObservable()
        viewModel!!.showResult.observe(this) {
            val dialog = TipDialog(this)
            dialog.setMessage(getString(R.string.sync_progress, it, viewModel!!.dataList.size))
            dialog.setLeftBtn(getString(R.string.cancel))
            dialog.setRightBtn(getString(R.string.btn_exit))
            dialog.setOnDialogClickListener(object : TipDialog.OnDialogClickListener() {
                override fun onRightClick() {
                    super.onRightClick()
                    finish()
                }
            })
            dialog.show()
        }
    }


    /**
     * 1、Google 日历同步入口
     * 当前实现：跳转到 Google Calendar 网页，后续可接入 Google Sign-In + Calendar API
     */
    private fun syncGoogle() {
        // 构建请求 Calendar 权限的 Sign-In 配置
        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail()
            .requestScopes(calendarScope)
            .build()
        val client = GoogleSignIn.getClient(this, gso)

        val account = GoogleSignIn.getLastSignedInAccount(this)
        if (account == null) {
            // 未登录，发起登录
            googleSignInLauncher.launch(client.signInIntent)
        } else {
            // 已登录，直接拉取 Calendar 列表
            fetchGoogleCalendars(account)
        }
    }

    /**
     * 2、Outlook 日历同步入口
     * 当前实现：跳转到 Outlook Calendar 网页，后续可接入 Microsoft OAuth + Graph API
     */
    private fun syncOutlook() {
        // 优先尝试使用 MSAL 获取令牌并调用 Graph API
        try {
            ensureMsalInitialized()
        } catch (e: Exception) {
            // 如果 MSAL 初始化失败（通常是缺失 msal_config.json 或 clientId 未配置），降级为网页打开
            val msg = "Microsoft OAuth not configured: ${e.localizedMessage}. Opening Outlook web."
            L.e(msg, e)
            showTip(msg)
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://outlook.live.com/calendar/"))
            startActivity(intent)
            return
        }

        val app = msalApp ?: run {
            showTip("MSAL initialization failed")
            return
        }

        // 尝试静默获取令牌
        app.getAccounts(object : IPublicClientApplication.LoadAccountsCallback {
            override fun onTaskCompleted(accounts: List<IAccount>?) {
                val account = accounts?.firstOrNull()
                if (account != null) {
                    val silentParams = AcquireTokenSilentParameters.Builder()
                        .forAccount(account)
                        .withScopes(msScopes)
                        .withCallback(object : AuthenticationCallback {
                            override fun onSuccess(authenticationResult: IAuthenticationResult) {
                                val token = authenticationResult.accessToken
                                fetchMicrosoftCalendars(token)
                            }

                            override fun onError(exception: MsalException) {
                                // 静默失败则走交互式登录
                                acquireMsTokenInteractive()
                            }

                            override fun onCancel() {
                                showTip("Microsoft Sign-In cancelled")
                            }
                        })
                        .build()
                    app.acquireTokenSilentAsync(silentParams)
                } else {
                    // 没有账户，交互式登录
                    acquireMsTokenInteractive()
                }
            }

            override fun onError(exception: MsalException) {
                // 获取账户失败，交互式登录
                acquireMsTokenInteractive()
            }
        })
    }

    private fun acquireMsTokenInteractive() {
        val app = msalApp ?: return
        val params = AcquireTokenParameters.Builder()
            .startAuthorizationFromActivity(this)
            .withScopes(msScopes)
            .withCallback(object : AuthenticationCallback {
                override fun onSuccess(authenticationResult: IAuthenticationResult) {
                    val token = authenticationResult.accessToken
                    fetchMicrosoftCalendars(token)
                }

                override fun onError(exception: MsalException) {
                    showTip("Microsoft Sign-In error: ${exception.localizedMessage ?: exception.javaClass.simpleName}")
                }

                override fun onCancel() {
                    showTip("Microsoft Sign-In cancelled")
                }
            })
            .build()
        app.acquireToken(params)
    }

    private fun ensureMsalInitialized() {
        if (msalApp != null) return
        // 通过配置文件创建多账户应用
        PublicClientApplication.createMultipleAccountPublicClientApplication(
            applicationContext,
            R.raw.msal_config,
            object : IPublicClientApplication.IMultipleAccountApplicationCreatedListener {
                override fun onCreated(application: IMultipleAccountPublicClientApplication) {
                    msalApp = application
                }

                override fun onError(exception: MsalException) {
                    val msg = "Microsoft OAuth init error: ${exception.localizedMessage ?: exception.javaClass.simpleName}"
                    L.e(msg, exception)
                    showTip(msg)
                    exception.printStackTrace()
                }
            }
        )
    }

    /**
     * 3、iCloud 日历同步入口
     * 当前实现：跳转到 iCloud Calendar 网页，后续可接入 CalDAV/ICS 订阅
     */
    private fun syncICloud() {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://www.icloud.com/calendar"))
        startActivity(intent)
    }

    /**
     * 4、提示输入日历 URL（ICS），支持普通 URL 或订阅 URL
     * @param isSubscribed true 表示订阅日历（自动更新），false 表示一次性导入
     */
    private fun promptCalendarUrl(isSubscribed: Boolean) {
        this.isSubscribed = isSubscribed
        this.urlInputLauncher.launch(Intent(this, InputURLActivity::class.java))
    }

    /**
     * 通过 ICS 链接导入一次性日历数据
     * 后续可以扩展为下载并解析 ICS，然后进入 SyncActivity 进行选择与同步
     */
    private fun syncCalendarUrl(url: String) {
        showLoading()
        ioExecutor.execute {
            try {
                val client = OkHttpClient()
                val request = Request.Builder()
                    .url(url)
                    .header("Accept", "text/calendar, text/plain, */*")
                    .build()
                val response = client.newCall(request).execute()
                if (!response.isSuccessful) {
                    runOnUiThread { showTip("ICS 下载失败: HTTP ${response.code}") }
                    return@execute
                }
                val stream = response.body?.byteStream()
                if (stream == null) {
                    runOnUiThread { showTip("ICS 下载失败: 空响应") }
                    return@execute
                }

                val calendars: List<ICalendar> = Biweekly.parse(stream).all()
                var totalEvents = 0
                val eventList = mutableListOf<ScheduleModelVo>()
                calendars.forEach { cal ->
                    val events: List<VEvent> = cal.events
                    totalEvents += events.size
                    events.take(10).forEach { evt ->
                        val title = evt.summary?.value ?: "(无标题)"
                        val startProp = evt.dateStart
                        val endProp = evt.dateEnd
                        val startDate = startProp?.value
                        val endDate = endProp?.value

                        // 近似判断是否为全天：若开始/结束时间均为午夜（00:00），则视为全天
                        fun isMidnight(d: Date): Boolean {
                            val cal = java.util.Calendar.getInstance()
                            cal.time = d
                            return cal.get(java.util.Calendar.HOUR_OF_DAY) == 0 && cal.get(java.util.Calendar.MINUTE) == 0 && cal.get(java.util.Calendar.SECOND) == 0
                        }

                        val allDay = when {
                            startDate == null -> false
                            endDate == null -> isMidnight(startDate)
                            else -> isMidnight(startDate) && isMidnight(endDate)
                        }

                        // 构建用于后续同步的数据对象（秒级时间戳）
                        val startEpochSec = startDate?.time?.div(1000)
                        val endEpochSec = (endDate ?: startDate)?.time?.div(1000)
                        eventList.add(
                            ScheduleModelVo(
                                title = title,
                                startTime = startEpochSec,
                                endTime = endEpochSec,
                                allDay = if (allDay) 1 else 0,
                                repeats = "0",
                            )
                        )
                    }
                }

                runOnUiThread {
                    dismissLoading()
                    onBack(eventList)
                }
            } catch (e: Exception) {
                runOnUiThread {
                    dismissLoading()
                    showTip("ICS 解析失败: ${e.localizedMessage ?: e.javaClass.simpleName}")
                }
            }
        }
    }

    /**
     * 订阅日历（ICS 订阅地址），保持后续自动更新
     * 后续可以扩展为保存订阅源、定时拉取更新
     */
    private fun syncSubscribedCalendar(url: String) {
//        showTip(getString(R.string.subscribed_calendar_import_tip))
//         TODO: 1) 校验订阅 URL；2) 保存订阅源；3) 后台定时拉取更新并合并到本地
        syncCalendarUrl(url)
    }

    private fun showTip(message: String) {
        val dialog = TipDialog(this)
        dialog.setMessage(message)
        dialog.setLeftBtn(getString(R.string.cancel))
        dialog.setRightBtn(getString(R.string.ok))
        dialog.show()
    }

    /**
     * 使用访问令牌读取 Google Calendar 列表
     */
    private fun fetchGoogleCalendars(account: GoogleSignInAccount) {
        ioExecutor.execute {
            try {
                // 通过 GoogleAuthUtil 获取访问令牌（需要用户同意）
                val token = GoogleAuthUtil.getToken(
                    applicationContext,
                    account.account!!, // Android Account
                    "oauth2:${calendarScope.scopeUri}"
                )

                // 使用 OkHttp 调用 CalendarList API
                val client = OkHttpClient()
                val request = Request.Builder()
                    .url("https://www.googleapis.com/calendar/v3/users/me/calendarList")
                    .addHeader("Authorization", "Bearer $token")
                    .build()
                val response = client.newCall(request).execute()
                val body = response.body?.string()

                if (!response.isSuccessful || body.isNullOrBlank()) {
                    runOnUiThread { showTip("Failed to fetch calendars: ${response.code}") }
                    return@execute
                }

                // 解析返回 JSON
                val list = Gson().fromJson(body, CalendarListResponse::class.java)
                val items = list.items ?: emptyList()
                val count = items.size

                // 选择 primary 日历（如有），否则使用第一个
                val selectedCalendarId = items.firstOrNull { it.primary == true }?.id
                    ?: items.firstOrNull()?.id

                if (selectedCalendarId.isNullOrBlank()) {
                    runOnUiThread {
                        showTip("未找到可用的 Google 日历")
                    }
                    return@execute
                }

                // 拉取选定日历的事件，最多 10 条，按开始时间排序
                val eventsUrl = "https://www.googleapis.com/calendar/v3/calendars/${
                    Uri.encode(selectedCalendarId)
                }/events?maxResults=1000&singleEvents=true&orderBy=startTime"
                val evRequest = Request.Builder()
                    .url(eventsUrl)
                    .addHeader("Authorization", "Bearer $token")
                    .build()
                val evResponse = client.newCall(evRequest).execute()
                val evBody = evResponse.body?.string()

                if (!evResponse.isSuccessful || evBody.isNullOrBlank()) {
                    runOnUiThread { showTip("获取 Google 事件失败: ${evResponse.code}") }
                    return@execute
                }

                val events = Gson().fromJson(evBody, GoogleEventListResponse::class.java).items ?: emptyList()

                fun parseGoogleEpochSeconds(dt: GoogleEventDateTime?): Long? {
                    if (dt == null) return null
                    // all-day 使用 date；非全天使用 dateTime
                    dt.date?.let {
                        return try {
                            val ld = java.time.LocalDate.parse(it, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
                            ld.atStartOfDay(java.time.ZoneId.systemDefault()).toEpochSecond()
                        } catch (_: Exception) {
                            null
                        }
                    }
                    dt.dateTime?.let {
                        return try {
                            // RFC3339，包含时区偏移
                            java.time.OffsetDateTime.parse(it).toEpochSecond()
                        } catch (_: Exception) {
                            // 兜底：按本地时区解析
                            try {
                                val ldt = java.time.LocalDateTime.parse(it.substring(0, 19), java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                                ldt.atZone(java.time.ZoneId.systemDefault()).toEpochSecond()
                            } catch (_: Exception) {
                                null
                            }
                        }
                    }
                    return null
                }
                // 构建 ScheduleModelVo 列表（秒级时间戳）
                val eventList = events.map { ev ->
                    val title = ev.summary ?: "(无标题)"
                    val startSec = parseGoogleEpochSeconds(ev.start)
                    val endSec = parseGoogleEpochSeconds(ev.end) ?: startSec
                    val isAllDay = ev.start?.date != null || ev.end?.date != null
                    ScheduleModelVo(
                        title = title,
                        startTime = startSec,
                        endTime = endSec,
                        allDay = if (isAllDay) 1 else 0,
                        repeats = "0",
                    )
                }

                runOnUiThread { onBack(eventList) }
            } catch (e: UserRecoverableAuthException) {
                // 需要用户授权，触发同意流程
                runOnUiThread { startActivity(e.intent) }
            } catch (e: Exception) {
                runOnUiThread { showTip("Google Calendar error: ${e.localizedMessage ?: e.javaClass.simpleName}") }
            }
        }
    }

    /**
     * 使用 Microsoft Graph API 读取 Outlook Calendar 列表
     */
    private fun fetchMicrosoftCalendars(accessToken: String) {
        ioExecutor.execute {
            try {
                val client = OkHttpClient()
                // 直接拉取事件列表（最多 10 条）
                val request = Request.Builder()
                    .url("https://graph.microsoft.com/v1.0/me/events?\$top=1000&\$select=subject,start,end,isAllDay&\$orderby=start/dateTime")
                    .addHeader("Authorization", "Bearer $accessToken")
                    .build()
                val response = client.newCall(request).execute()
                val body = response.body?.string()

                if (!response.isSuccessful || body.isNullOrBlank()) {
                    runOnUiThread { showTip("获取 Outlook 事件失败: ${response.code}") }
                    return@execute
                }

                val list = Gson().fromJson(body, GraphEventListResponse::class.java)
                val events = list.value ?: emptyList()

                fun parseGraphEpochSeconds(dt: GraphDateTime?, allDay: Boolean): Long? {
                    if (dt?.dateTime.isNullOrBlank()) return null
                    val str = dt.dateTime
                    return try {
                        // 优先尝试 OffsetDateTime（如果包含偏移）
                        java.time.OffsetDateTime.parse(str).toEpochSecond()
                    } catch (_: Exception) {
                        try {
                            val ldt = java.time.LocalDateTime.parse(str.take(19), java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                            ldt.atZone(java.time.ZoneId.systemDefault()).toEpochSecond()
                        } catch (_: Exception) {
                            null
                        }
                    }
                }

                val eventList = events.map { ev ->
                    val title = ev.subject ?: "(无标题)"
                    val isAllDay = ev.isAllDay == true
                    val startSec = parseGraphEpochSeconds(ev.start, isAllDay)
                    val endSec = parseGraphEpochSeconds(ev.end, isAllDay) ?: startSec
                    ScheduleModelVo(
                        title = title,
                        startTime = startSec,
                        endTime = endSec,
                        allDay = if (isAllDay) 1 else 0,
                        repeats = "0",
                    )
                }

                runOnUiThread { onBack(eventList) }
            } catch (e: Exception) {
                runOnUiThread { showTip("Microsoft Graph error: ${e.localizedMessage ?: e.javaClass.simpleName}") }
            }
        }
    }

    private fun onBack(data: List<ScheduleModelVo>) {
        val dialog = TipDialog(this)
        dialog.setTitle("导入预览")
        dialog.setMessage("事件总数: ${data.size}")
        dialog.setLeftBtn(getString(R.string.cancel))
        dialog.setRightBtn(getString(R.string.ok))
        dialog.setOnDialogClickListener(object : TipDialog.OnDialogClickListener() {
            override fun onRightClick() {
                super.onRightClick()
                viewModel!!.uploadEvent(data)
            }
        })
        dialog.show()
    }

    // 简化的 CalendarList 数据模型
    private data class CalendarListResponse(
        @SerializedName("items") val items: List<CalendarListEntry>?,
    )

    private data class CalendarListEntry(
        @SerializedName("id") val id: String?,
        @SerializedName("summary") val summary: String?,
        @SerializedName("primary") val primary: Boolean?,
    )

    // Graph 数据模型
    private data class GraphCalendarListResponse(
        @SerializedName("value") val value: List<GraphCalendarEntry>?,
    )

    private data class GraphCalendarEntry(
        @SerializedName("id") val id: String?,
        @SerializedName("name") val name: String?,
    )

    // Google 事件
    private data class GoogleEventListResponse(
        @SerializedName("items") val items: List<GoogleEvent>?,
    )

    private data class GoogleEvent(
        @SerializedName("summary") val summary: String?,
        @SerializedName("start") val start: GoogleEventDateTime?,
        @SerializedName("end") val end: GoogleEventDateTime?,
    )

    private data class GoogleEventDateTime(
        @SerializedName("dateTime") val dateTime: String?,
        @SerializedName("date") val date: String?,
    )

    // Outlook 事件
    private data class GraphEventListResponse(
        @SerializedName("value") val value: List<GraphEvent>?,
    )

    private data class GraphEvent(
        @SerializedName("subject") val subject: String?,
        @SerializedName("isAllDay") val isAllDay: Boolean?,
        @SerializedName("start") val start: GraphDateTime?,
        @SerializedName("end") val end: GraphDateTime?,
    )

    private data class GraphDateTime(
        @SerializedName("dateTime") val dateTime: String?,
        @SerializedName("timeZone") val timeZone: String?,
    )
}